package org.codehaus.activemq.service.impl;

import java.io.Serializable;
import org.codehaus.activemq.service.QueueList;
import org.codehaus.activemq.service.QueueListEntry;

public final class DefaultQueueList
  implements QueueList, Cloneable, Serializable
{
  private transient DefaultQueueListEntry header = new DefaultQueueListEntry(null, null, null);
  private transient int size = 0;

  public DefaultQueueList()
  {
    this.header.next = (this.header.previous = this.header);
  }

  public synchronized Object getFirst()
  {
    if (this.size == 0) {
      return null;
    }
    return this.header.next.element;
  }

  public synchronized Object getLast()
  {
    if (this.size == 0) {
      return null;
    }
    return this.header.previous.element;
  }

  public synchronized Object removeFirst()
  {
    if (this.size == 0) {
      return null;
    }
    Object first = this.header.next.element;
    remove(this.header.next);
    return first;
  }

  public void rotate()
  {
    if (this.size > 1) {
      Object obj = removeFirst();
      if (obj != null)
        addLast(obj);
    }
  }

  public synchronized Object removeLast()
  {
    if (this.size == 0) {
      return null;
    }
    Object last = this.header.previous.element;
    remove(this.header.previous);
    return last;
  }

  public synchronized QueueListEntry addFirst(Object o)
  {
    return addBefore(o, this.header.next);
  }

  public synchronized QueueListEntry addLast(Object o) {
    return addBefore(o, this.header);
  }

  public synchronized boolean contains(Object o) {
    return indexOf(o) != -1;
  }

  public synchronized int size() {
    return this.size;
  }

  public synchronized boolean isEmpty() {
    return this.size == 0;
  }

  public synchronized QueueListEntry add(Object o) {
    return addBefore(o, this.header);
  }

  public synchronized boolean remove(Object o) {
    if (o == null) {
      for (DefaultQueueListEntry e = this.header.next; e != this.header; e = e.next) {
        if (e.element == null) {
          remove(e);
          return true;
        }
      }
    }
    else {
      for (DefaultQueueListEntry e = this.header.next; e != this.header; e = e.next) {
        if (o.equals(e.element)) {
          remove(e);
          return true;
        }
      }
    }
    return false;
  }

  public synchronized void clear() {
    this.header.next = (this.header.previous = this.header);
    this.size = 0;
  }

  public synchronized Object get(int index)
  {
    return entry(index).element;
  }

  public synchronized Object set(int index, Object element) {
    DefaultQueueListEntry e = entry(index);
    Object oldVal = e.element;
    e.element = element;
    return oldVal;
  }

  public synchronized void add(int index, Object element) {
    addBefore(element, index == this.size ? this.header : entry(index));
  }

  public synchronized Object remove(int index) {
    DefaultQueueListEntry e = entry(index);
    remove(e);
    return e.element;
  }

  public synchronized DefaultQueueListEntry entry(int index) {
    if ((index < 0) || (index >= this.size)) {
      throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size);
    }
    DefaultQueueListEntry e = this.header;
    if (index < this.size / 2) {
      for (int i = 0; i <= index; i++) {
        e = e.next;
      }
    }
    else {
      for (int i = this.size; i > index; i--) {
        e = e.previous;
      }
    }
    return e;
  }

  public synchronized int indexOf(Object o)
  {
    int index = 0;
    if (o == null) {
      for (DefaultQueueListEntry e = this.header.next; e != this.header; e = e.next) {
        if (e.element == null) {
          return index;
        }
        index++;
      }
    }
    else {
      for (DefaultQueueListEntry e = this.header.next; e != this.header; e = e.next) {
        if (o.equals(e.element)) {
          return index;
        }
        index++;
      }
    }
    return -1;
  }

  public synchronized int lastIndexOf(Object o) {
    int index = this.size;
    if (o == null) {
      for (DefaultQueueListEntry e = this.header.previous; e != this.header; e = e.previous) {
        index--;
        if (e.element == null) {
          return index;
        }
      }
    }
    else {
      for (DefaultQueueListEntry e = this.header.previous; e != this.header; e = e.previous) {
        index--;
        if (o.equals(e.element)) {
          return index;
        }
      }
    }
    return -1;
  }

  public synchronized QueueListEntry getFirstEntry() {
    DefaultQueueListEntry result = this.header.next;
    return result != this.header ? result : null;
  }

  public synchronized QueueListEntry getLastEntry() {
    DefaultQueueListEntry result = this.header.previous;
    return result != this.header ? result : null;
  }

  public QueueListEntry getNextEntry(QueueListEntry node) {
    DefaultQueueListEntry entry = (DefaultQueueListEntry)node;
    if (entry == null) {
      return null;
    }
    DefaultQueueListEntry result = entry.next;
    return (result != entry) && (result != this.header) ? result : null;
  }

  public QueueListEntry getPrevEntry(QueueListEntry node) {
    DefaultQueueListEntry entry = (DefaultQueueListEntry)node;
    if (entry == null) {
      return null;
    }
    DefaultQueueListEntry result = entry.previous;
    return (result != entry) && (result != this.header) ? result : null;
  }

  public QueueListEntry addBefore(Object o, QueueListEntry node) {
    DefaultQueueListEntry e = (DefaultQueueListEntry)node;
    DefaultQueueListEntry newLinkedListEntry = new DefaultQueueListEntry(o, e, e.previous);
    newLinkedListEntry.previous.next = newLinkedListEntry;
    newLinkedListEntry.next.previous = newLinkedListEntry;
    this.size += 1;
    return newLinkedListEntry;
  }

  public void remove(QueueListEntry node) {
    DefaultQueueListEntry e = (DefaultQueueListEntry)node;
    if (e == this.header) {
      return;
    }
    e.previous.next = e.next;
    e.next.previous = e.previous;
    this.size -= 1;
  }

  public synchronized Object clone()
  {
    DefaultQueueList clone = new DefaultQueueList();

    clone.header = new DefaultQueueListEntry(null, null, null);
    clone.header.next = (clone.header.previous = clone.header);
    clone.size = 0;

    for (DefaultQueueListEntry e = this.header.next; e != this.header; e = e.next) {
      clone.add(e.element);
    }
    return clone;
  }

  public synchronized Object[] toArray() {
    Object[] result = new Object[this.size];
    int i = 0;
    for (DefaultQueueListEntry e = this.header.next; e != this.header; e = e.next) {
      result[(i++)] = e.element;
    }
    return result;
  }
}